package com.yami.shop.im.socket;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yami.shop.bean.model.ShopDetail;
import com.yami.shop.bean.model.User;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.Json;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.im.bean.ImMsgBizUserHistory;
import com.yami.shop.im.constants.Constant;
import com.yami.shop.im.constants.MsgCode;
import com.yami.shop.im.constants.SendType;
import com.yami.shop.im.hanlder.SecurityHandler;
import com.yami.shop.im.param.MessageInfo;
import com.yami.shop.im.service.ImMsgBizUserHistoryService;
import com.yami.shop.im.vo.MsgItem;
import com.yami.shop.im.vo.ShopDetailEmployee;
import com.yami.shop.security.multishop.model.YamiShopUser;
import com.yami.shop.service.ShopDetailService;
import com.yami.shop.service.UserService;
import com.yami.shop.sys.common.model.ShopEmployee;
import com.yami.shop.sys.common.service.ShopEmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 商家收到的消息
 * 10,"无法获取用户信息"
 * 11,"账户已在别处登录，请刷新"
 * 12,"对方不在线"
 * 13, "内容格式有误"
 *
 * @author Lzk
 */
@ServerEndpoint("/im/websocket/shop/{token}/{shopId}/{employeeId}")
@Component
@Slf4j
public class ShopWebSocketServer {

    /**
     * shopId为key
     */
    public static final ConcurrentHashMap<String, Session> SHOP_EMPLOYEE_MAP = new ConcurrentHashMap<>(256);

    private static SecurityHandler securityHandler;

    private static ShopDetailService shopDetailService;

    private static ShopEmployeeService shopEmployeeService;

    private static UserService userService;

    private static ImMsgBizUserHistoryService imMsgBizUserHistoryService;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        shopDetailService = applicationContext.getBean(ShopDetailService.class);
        shopEmployeeService = applicationContext.getBean(ShopEmployeeService.class);
        securityHandler = applicationContext.getBean(SecurityHandler.class);
        userService = applicationContext.getBean(UserService.class);
        imMsgBizUserHistoryService = applicationContext.getBean(ImMsgBizUserHistoryService.class);
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws IOException {
        log.info("创建连接");
        YamiShopUser shopUser = securityHandler.getShopUser(token);
        if (shopUser == null) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取用户信息")));
            }
            return;
        }

        // 如果原来就有token
        Session oldSession = SHOP_EMPLOYEE_MAP.get(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE + shopUser.getEmployeeId());
        if (oldSession != null) {
            // 客户端收到11，关闭ws链接，弹出提示
            if (oldSession.isOpen()) {
                oldSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.LOGIN_IN_OTHER_PLACE, "账户已在别处登录，请刷新")));
                oldSession.close();
            }
        }

        SHOP_EMPLOYEE_MAP.put(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE + shopUser.getEmployeeId(), session);
        RedisUtil.set(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE + shopUser.getEmployeeId(), "", 0);
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("token") String token, @PathParam( "shopId") Long shopId, @PathParam( "employeeId") Long employeeId) {
        log.info("关闭连接");
        SHOP_EMPLOYEE_MAP.remove(Constant.SHOP_EMPLOYEE_PREFIX + shopId + Constant.UNDERLINE + employeeId);
        RedisUtil.del(Constant.SHOP_EMPLOYEE_PREFIX + shopId + Constant.UNDERLINE + employeeId);
    }

    /**
     * 连接错误调用的方法
     */
    @OnError
    public void onError(Session session, @PathParam("token") String token, Throwable error, @PathParam( "shopId") Long shopId, @PathParam( "employeeId") Long employeeId) {
        log.error("发生错误");
        error.printStackTrace();
        SHOP_EMPLOYEE_MAP.remove(Constant.SHOP_EMPLOYEE_PREFIX + shopId + Constant.UNDERLINE + employeeId);
        RedisUtil.del(Constant.SHOP_EMPLOYEE_PREFIX + shopId + Constant.UNDERLINE + employeeId);
    }

    /**
     * 收到客户端消息后调用的方法，该消息由商家发过来
     *
     * @param sourceMessage 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String sourceMessage, @PathParam("token") String token, Session session) throws IOException {
        YamiShopUser shopUser = securityHandler.getShopUser(token);
        if (Objects.isNull(shopUser)) {
            log.error("无法获取用户信息");
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取用户信息")));
            }
            return;
        }

        if (StrUtil.isBlank(sourceMessage)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "内容格式有误")));
            }
            return;
        }

        log.info("sourceMessage: " + sourceMessage);

        MessageInfo messageInfo = Json.parseObject(sourceMessage, MessageInfo.class);

        if (Objects.isNull(messageInfo)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "内容格式有误")));
            }
            return;
        }

        // 保持心跳
        if (StrUtil.equals(messageInfo.getContent(), Constant.HEART_BEAT)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HEART_BEAT, "保持心跳")));
            }
            return;
        }

        String userId = messageInfo.getToId();
        User user = userService.getUserByUserId(userId);
        if (Objects.isNull(user)) {
            user = new User();
            user.setUserId(userId);
            user.setUserName("*用户已注销*");
            user.setNickName("*用户已注销*");
        }

        sendToUser(messageInfo, shopUser, user, session);
    }

    public void sendToUser(MessageInfo messageInfo, YamiShopUser shopUser, User user, Session session) throws IOException {
        ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopUser.getShopId());

        // 获取消息历史--从数据库获取用户、商家和平台的聊天记录
        if (Objects.nonNull(messageInfo.getHistory()) && Objects.equals(messageInfo.getHistory(), true)) {
            Page<MsgItem> msgItemPage = imMsgBizUserHistoryService.getShopUserHistory(shopUser, messageInfo, user, shopDetail);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HISTORY, msgItemPage)));
            }
            return;
        }

        // 标记消息已读
        if (Objects.nonNull(messageInfo.getRead()) && Objects.equals(messageInfo.getRead(), true)) {
            if (Objects.nonNull(messageInfo.getMsgIds()) && !messageInfo.getMsgIds().isEmpty()) {
                List<ImMsgBizUserHistory> imMsgBizUserHistories = imMsgBizUserHistoryService.markShopUserRead(messageInfo, shopUser, user.getUserId());
                if (!imMsgBizUserHistories.isEmpty()) {
                    if (session.isOpen()) {
                        session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
                    }
                    Session userSession = UserWebSocketServer.USER_MAP.get(Constant.USER_PREFIX + messageInfo.getToId());
                    if (Objects.nonNull(userSession)) {
                        if (userSession.isOpen()) {
                            userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.READ, messageInfo.getMsgIds())));
                        }
                    }
                }
            }
            return;
        }

        if (Objects.nonNull(messageInfo.getOnlineOrOffline()) && Objects.equals(messageInfo.getOnlineOrOffline(), true)) {
            Set<String> set = RedisUtil.keys(Constant.ONLINE_USER_PREFIX + user.getUserId() + Constant.UNDERLINE + "*");
            MsgItem msgItem = new MsgItem();
            msgItem.setChatType(SendType.SHOP);
            msgItem.setUserId(user.getUserId());
            if (!set.isEmpty()) {
                msgItem.setOnlineOrOffline(true);
            } else {
                msgItem.setOnlineOrOffline(false);
            }
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.ONLINE_OR_OFFLINE, msgItem)));
            }
        }

        Long employeeId = RedisUtil.getLongValue(Constant.IN_CHARGE_SHOP + shopUser.getShopId() + Constant.UNDERLINE + messageInfo.getToId());
        if (Objects.nonNull(employeeId)) {
            if (!Objects.equals(employeeId, shopUser.getEmployeeId())) {
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.NOT_YOUR_CLIENT, "不是你负责的客户")));
                }
                return;
            }
        }

        // 获取可转接客服列表
        if (Objects.nonNull(messageInfo.getEmployeeList()) && Objects.equals(messageInfo.getEmployeeList(), true)) {
            List<ShopDetailEmployee> listEmployee = listTransfer(shopUser, shopDetail);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.EMPLOYEE_ONLINE, listEmployee)));
            }
            return;
        }

        // 转接客服
        if (Objects.nonNull(messageInfo.getChangeTo()) && Objects.equals(messageInfo.getChangeTo(), true)) {
            transferShopEmployee(messageInfo, shopUser, user, session, shopDetail);
            return;
        }

        // 发送消息
        if (Objects.nonNull(messageInfo.getContent()) && !messageInfo.getContent().isEmpty()) {
            sendMessage(messageInfo, shopUser, user, shopDetail, employeeId);
        }
    }

    private void sendMessage(MessageInfo messageInfo, YamiShopUser shopUser, User user, ShopDetail shopDetail, Long employeeId) throws IOException {
        ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveShopUserHistory(messageInfo, shopUser);
        // 在线的话发送消息，不在线的话留言
        Session userSession = UserWebSocketServer.USER_MAP.get(Constant.USER_PREFIX + messageInfo.getToId());
        if (Objects.nonNull(userSession)) {
            if (Objects.isNull(employeeId)) {
                RedisUtil.set(Constant.IN_CHARGE_SHOP + shopUser.getShopId() + Constant.UNDERLINE + messageInfo.getToId(), shopUser.getEmployeeId(), 0);
            }
            MsgItem msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopDetail, user, imMsgBizUserHistory);
            if (userSession.isOpen()) {
                userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
            }
            Set<String> set2 = RedisUtil.keys(Constant.ONLINE_USER_PREFIX + messageInfo.getToId() + Constant.UNDERLINE + "*");
            List<Session> listUser2 = new ArrayList<>();
            for (String key : set2) {
                if (key.startsWith(Constant.ONLINE_USER_PREFIX + messageInfo.getToId() + Constant.UNDERLINE)) {
                    listUser2.add(UserOnlineWebSocketServer.USER_ONLINE_MAP.get(key));
                }
            }
            if (!listUser2.isEmpty()) {
                MsgItem msgItem2 = new MsgItem();
                msgItem2.setNewMessage(true);
                for (Session userSession2 : listUser2) {
                    if (Objects.nonNull(userSession2)) {
                        if (userSession2.isOpen()) {
                            userSession2.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem2)));
                        }
                    }
                }
            }
        }
    }

    private void transferShopEmployee(MessageInfo messageInfo, YamiShopUser shopUser, User user, Session session, ShopDetail shopDetail) throws IOException {
        if (Objects.nonNull(messageInfo.getEmployeeId())) {
            Session userSession = UserWebSocketServer.USER_MAP.get(Constant.USER_PREFIX + messageInfo.getToId());
            Session shopSession = SHOP_EMPLOYEE_MAP.get(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE + messageInfo.getEmployeeId());
            if (Objects.nonNull(shopSession)) {
                RedisUtil.set(Constant.IN_CHARGE_SHOP + shopUser.getShopId() + Constant.UNDERLINE + messageInfo.getToId(), messageInfo.getEmployeeId(), 0);
                ShopEmployee shopEmployee = shopEmployeeService.getShopEmployeeById(messageInfo.getEmployeeId());
                ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveShopChangeEmployeeReply(shopUser, shopEmployee, user);
                MsgItem msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopDetail, user, imMsgBizUserHistory);
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CHANGE_EMPLOYEE_SUCCESS, msgItem)));
                }
                // 在线的话发送消息，不在线的话留言
                if (Objects.nonNull(userSession) && userSession.isOpen()) {
                    userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CHANGE_EMPLOYEE_SUCCESS, msgItem)));
                }

                if (shopSession.isOpen()) {
                    shopSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CHANGE_EMPLOYEE_SUCCESS, msgItem)));
                }
            }
        }
    }

    @NotNull
    private List<ShopDetailEmployee> listTransfer(YamiShopUser shopUser, ShopDetail shopDetail) {
        Set<String> set = RedisUtil.keys(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE + "*");
        List<ShopDetailEmployee> listEmployee = new ArrayList<>();
        for (String key : set) {
            if (key.startsWith(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE)) {
                if (!Objects.equals(Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE + shopUser.getEmployeeId(), key)) {
                    long tempLength = (Constant.SHOP_EMPLOYEE_PREFIX + shopUser.getShopId() + Constant.UNDERLINE).length();
                    Long tempEmployeeId = Long.valueOf(key.substring((int) tempLength));
                    ShopEmployee shopEmployee = shopEmployeeService.getShopEmployeeById(tempEmployeeId);
                    ShopDetailEmployee shopDetailEmployee = new ShopDetailEmployee();
                    shopDetailEmployee.setShopName(shopDetail.getShopName());
                    shopDetailEmployee.setPic(shopDetail.getShopLogo());
                    shopDetailEmployee.setShopId(shopDetail.getShopId());
                    shopDetailEmployee.setEmployeeId(shopEmployee.getEmployeeId());
                    shopDetailEmployee.setEmployeeNickname(shopEmployee.getNickname());
                    listEmployee.add(shopDetailEmployee);
                }
            }
        }
        return listEmployee;
    }

}
